package com.ghp.admin.config;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.module.paramnames.ParameterNamesModule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

/**
 * 自定义RedisTemplate
 * 默认的RedisTemplate存储对象类型的数据会发生乱码，这里我们对对象数据进行应该序列化
 */
@Configuration
public class RedisTemplateConfig {

    /**
     * 自定义 RedisTemplate 对象，并将其放到 IOC 容器，使其替代默认的RedisTemplate
     * @param factory
     * @return
     */
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        // 1、创建RedisTemplate对象
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        // 设置RedisTemplate对象连接工厂，用于获取Redis连接
        redisTemplate.setConnectionFactory(factory);

        // 2、配置 Object 类型和 String 类型的序列化器
        // 创建一个 Jackson2JsonRedisSerializer对象，用于将对象序列化为 JSON 格式
        Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
        // 创建StringRedisSerializer对象，用于StringRedisSerializer
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();

        // 3、设置序列化和反序列化的行为
        // 创建一个 ObjectMapper 对象，用于配置 JSON 序列化和反序列化的行为
        ObjectMapper mapper = new ObjectMapper();
        // 禁用将日期序列化为时间戳的功能（解决jackson反序列化localDatetime）
        mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
        // 配置日期和时间类的序列化和反序列化
        mapper.registerModule(new Jdk8Module())
                .registerModule(new JavaTimeModule())
                .registerModule(new ParameterNamesModule());
        // 设置 ObjectMapper 对象的属性可见性，使其可以访问所有属性
        mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        // 设置对象的默认类型信息，用于反序列化时确定对象的实际类型
        mapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance,
                ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
        // 将自定义的 ObjectMapper 对象应用于 Jackson2JsonRedisSerializer 序列化器中
        jackson2JsonRedisSerializer.setObjectMapper(mapper);

        // 4、配置序列化
        // key采用String的序列化方式
        redisTemplate.setKeySerializer(stringRedisSerializer);
        // hash的key也采用String的序列化方式
        redisTemplate.setHashKeySerializer(stringRedisSerializer);
        // value序列化方式采用Object的序列化方式
        redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
        // hash的value序列化方式采用Object的序列化方式
        redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);

        // 5、检查 RedisTemplate 的必要属性是否被正确设置
        // 在设置了Redis连接工厂和序列化器之后，初始化RedisTemplate对象的其他属性，确保RedisTemplate对象的所有必要属性都被正确设置
        redisTemplate.afterPropertiesSet();

        // 6、将自定义好的 RedisTemplate 返回
        return redisTemplate;
    }

}
